import java.io.*;
import java.util.*;

public class Lexical {
    // 创建一个Map来存储字符串和对应的TokenCode
    Map<String, TokenCode> tokenCodeMap;
    Set<String> keyword;
    Set<Character> boundary;
    Set<Character> operation;
    Set<Character> operationBeginChar;
    Set<String> singleCharacter;
    Set<String> operationOf2Char;
    boolean firstSingleQuotes;
    SymbolTable symbolTable = new SymbolTable();
    FSM miniDFA;
    int lineNum = 0;

    public Lexical() {
        miniDFA = FSM.createNFA().convertToDFA().minimizeDFA();
        keyword = new HashSet<>(Arrays.asList("int", "void", "return", "const", "main","float","char","struct","union","switch","default","break","case"));
        boundary = new HashSet<>(Arrays.asList('(', ')', '{', '}', ';', ',','\'',':'));
        operation = new HashSet<>(Arrays.asList('+', '-', '*', '/', '%', '=', '>', '<','!'));
        operationBeginChar = new HashSet<>(Arrays.asList('=', '<', '>', '!', '&', '|'));
        operationOf2Char = new HashSet<>(Arrays.asList("==", "<=", ">=", "!=", "&&", "||"));
        singleCharacter =new HashSet<>(AlphabetUtil.getPrintableChars());
        tokenCodeMap = new HashMap<String, TokenCode>();
        firstSingleQuotes=true;
        // 关键字映射
        tokenCodeMap.put("int", TokenCode.KW_INT);         // int关键字
        tokenCodeMap.put("float", TokenCode.KW_FLOAT);         // float关键字
        tokenCodeMap.put("char", TokenCode.KW_CHAR);         // char关键字
        tokenCodeMap.put("void", TokenCode.KW_VOID);       // void关键字
        tokenCodeMap.put("return", TokenCode.KW_RETURN);   // return关键字
        tokenCodeMap.put("const", TokenCode.KW_CONST);     // const关键字
        tokenCodeMap.put("main", TokenCode.KW_MAIN);       // main关键字
        tokenCodeMap.put("struct", TokenCode.KW_STRUCT);       // struct关键字
        tokenCodeMap.put("union", TokenCode.KW_UNION);       // union关键字
        tokenCodeMap.put("switch", TokenCode.KW_SWITCH);       // switch关键字
        tokenCodeMap.put("default", TokenCode.KW_DEFAULT);   // default关键字
        tokenCodeMap.put("break", TokenCode.KW_BREAK);   // break关键字
        tokenCodeMap.put("case", TokenCode.KW_CASE);   // case关键字

        // 运算符映射
        tokenCodeMap.put("!",TokenCode.OP_NOT);            // 否定运算符
        tokenCodeMap.put("+", TokenCode.OP_PLUS);          // 加号运算符
        tokenCodeMap.put("-", TokenCode.OP_MINUS);         // 减号运算符
        tokenCodeMap.put("*", TokenCode.OP_MULTIPLY);      // 乘号运算符
        tokenCodeMap.put("/", TokenCode.OP_DIVIDE);        // 除号运算符
        tokenCodeMap.put("%", TokenCode.OP_PERCENT);       // 取模运算符
        tokenCodeMap.put("=", TokenCode.OP_ASSIGN);        // 赋值运算符
        tokenCodeMap.put(">", TokenCode.OP_GT);            // 大于运算符
        tokenCodeMap.put("<", TokenCode.OP_LT);            // 小于运算符
        tokenCodeMap.put("==", TokenCode.OP_EQUAL);        // 等于运算符
        tokenCodeMap.put("<=", TokenCode.OP_LEQ);          // 小于等于运算符
        tokenCodeMap.put(">=", TokenCode.OP_GEQ);          // 大于等于运算符
        tokenCodeMap.put("!=", TokenCode.OP_NEQ);          // 不等于运算符
        tokenCodeMap.put("&&", TokenCode.OP_AND);          // 逻辑与运算符
        tokenCodeMap.put("||", TokenCode.OP_OR);           // 逻辑或运算符

        // 界符映射
        tokenCodeMap.put("(", TokenCode.SE_LPARENT);       // 左圆括号
        tokenCodeMap.put(")", TokenCode.SE_RPARENT);       // 右圆括号
        tokenCodeMap.put("{", TokenCode.SE_LBRACES);       // 左大括号
        tokenCodeMap.put("}", TokenCode.SE_RBARCES);       // 右大括号
        tokenCodeMap.put(";", TokenCode.SE_SEMICOLON);     // 分号
        tokenCodeMap.put(":", TokenCode.SE_COLON);         // 冒号
        tokenCodeMap.put(",", TokenCode.SE_COMMA);         // 逗号
        tokenCodeMap.put("'", TokenCode.SE_SINGLE_QUOTE);         // 单引号号
    }

    // 分析Token的方法
    public void analyseToken(String token) {
        if (token.length() <= 0) {
            return;
        }

        // 判断关键字
        if (Util.isAllLetter(token) && keyword.contains(Util.toLower(token))) {
            Util.printToken(token, tokenCodeMap.get(token), lineNum);
            return;
        }
        /**
         * 思路：
         * 1. 先用正则表达式构造NFA
         * 2. NFA转成DFA
         * 3. DFA最小化
         * 4. 把token扔进最小化的DFA看看返回什么状态---> 标识符!或变量!，以及错误状态，只有这三种可能
         * 注意：
         * 1. 这里只需要返回TokenCode即可
         */
        TokenCode tokenCode = miniDFA.getTokenCode( token);
        Util.printToken(token, tokenCode, lineNum);
        if (tokenCode == TokenCode.IDN) {
            symbolTable.addSymbol(token);
        }
    }


    public void lexicalAnalysis(String fileName) {
        PushbackReader file;
        BufferedWriter writer;
        try
        {
            file = new PushbackReader(new FileReader(fileName));
            writer = new BufferedWriter(new FileWriter(Util.getLexicalTxtPath()));
            writer.write(""); // 清空文件内容

            int charCode;
            StringBuilder token = new StringBuilder();

            while ((charCode = file.read()) != -1) {
//                fileTemp.read();
                char c = (char) charCode;

                if (c == ' ' || c == '\t' || c == '\n'|| c == '\r') {
                    analyseToken(token.toString());
                    token.setLength(0); // 清空token
                    if (c == '\n') {
                        lineNum++;
                    }
                } else if (boundary.contains(c)) { // 界符
                    analyseToken(token.toString());
                    token.setLength(0);
                    token.append(c);
                    Util.printToken(token.toString(), tokenCodeMap.get(token.toString()), lineNum);
                    if (c == '\''){
                        if (firstSingleQuotes){
                            firstSingleQuotes = false;
                            StringBuilder single= new StringBuilder();
                            int nextCharCode = file.read();
                            char nextChar = (char) nextCharCode;
                            while (nextChar!='\''){
                                single.append(nextChar);
                                nextCharCode = file.read();
                                nextChar = (char) nextCharCode;
                            }
                            if (singleCharacter.contains(single.toString()) ){
                                Util.printToken(single.toString(), TokenCode.CHAR, lineNum);
                            }else {
                                Util.printToken(single.toString(), TokenCode.UNDEFINED, lineNum);
                            }
                            Util.printToken(String.valueOf(nextChar), tokenCodeMap.get(String.valueOf(nextChar)), lineNum);
                        }else {
                            firstSingleQuotes = true;
                        }

                    }
                    firstSingleQuotes = true;
                    token.setLength(0);
                } else if (operation.contains(c) || operationBeginChar.contains(c)) { // 运算符
                    analyseToken(token.toString());
                    token.setLength(0);

                    if (operationBeginChar.contains(c)) {
                        int nextCharCode = file.read();
                        char nextChar = (char) nextCharCode;
                        String tryOp = "" + c + nextChar;
                        if (operationOf2Char.contains(tryOp)) {
                            token.append(tryOp);
                            Util.printToken(token.toString(), tokenCodeMap.get(token.toString()), lineNum);
                        } else if (operation.contains(c)) {
                            token.append(c);
                            Util.printToken(token.toString(), tokenCodeMap.get(token.toString()), lineNum);
                            file.unread(nextCharCode);
                        } else {
                            token.append(c);
                            Util.printToken(token.toString(), TokenCode.UNDEFINED, lineNum);
                            file.unread(nextCharCode);
                        }
                    } else {
                        token.append(c);
                        Util.printToken(token.toString(), tokenCodeMap.get(token.toString()), lineNum);
                    }
                    token.setLength(0);
                } else {
                    token.append(c);
                }
            }
//            symbolTable.printTable();
            Util.removeTrailingEmptyLine();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Error: " + fileName + " could not open!");
        }
    }
    public static void main(String[] args) {
        String fileName;
        if (args.length == 0) {
            fileName = "test.sy";
        } else {
            fileName = args[0];
        }
        Lexical lexical = new Lexical();//初始化
        lexical.lexicalAnalysis(fileName);
    }


}
